<template>
  <el-form class="basic-form" :inline="true" :model="form" ref="formRef">
    <div class="row">
      <div class="col" v-for="item in getFormItem" :key="item.field">
        <el-form-item
          style="width: 100%"
          :label="item.label"
          :prop="item.field"
          :label-width="item.labelWidth"
          :rules="[
            {
              required: item.componentProps && item.componentProps.required ? true : false,
              message: `${item.label}必填`,
              trigger: 'change'
            }
          ]"
        >
          <!-- 输入框 Input -->
          <el-input
            v-if="item.component === 'Input'"
            @input="$forceUpdate()"
            :id="item.field"
            :placeholder="item.placeholder || '请输入'"
            v-model="form[item.field]"
            style="width: 100%"
            v-bind="item.componentProps"
          />
          <!-- 日期选择器 DatePicker -->
          <el-date-picker
            v-if="item.component === 'DatePicker'"
            @focus="$forceUpdate()"
            @blur="$forceUpdate()"
            @change="(value) => datepickerChange(value, item)"
            :type="item.type"
            :value-format="item.valueFormat || 'YYYY-MM-DD HH:mm:ss'"
            :start-placeholder="item.startPlaceholder"
            :end-placeholder="item.endPlaceholder"
            v-model="form[item.field]"
            style="width: 100%"
            v-bind="item.componentProps"
          />
          <!-- 自定义选择器 Select -->
          <el-select
            v-if="item.component === 'Select'"
            @change="selectChange(item)"
            :id="item.field"
            v-model="form[item.field]"
            style="width: 100%"
            v-bind="item.componentProps"
          >
            <el-option
              v-if="item.componentProps && item.componentProps.hasAll"
              label="全部"
              value="全部"
            />
            <el-option
              v-for="(i, index) in item.options || []"
              :key="index"
              :value="i[(item.options && item.options.value) || 'value']"
              :label="i[(item.options && item.options.label) || 'label']"
            />
          </el-select>
          <!-- 接口选择器 ApiSelect -->
          <el-select
            v-if="item.component === 'ApiSelect'"
            @change="selectChange(item)"
            :id="item.field"
            v-model="form[item.field]"
            style="width: 100%"
            v-bind="item.componentProps"
          >
            <el-option
              v-if="item.componentProps && item.componentProps.hasAll"
              label="全部"
              value="全部"
            />
            <el-option
              v-for="(i, index) in item[`${item.field}_options`]"
              :key="`${i}_${index}`"
              :value="i[(item.options && item.options.value) || 'value']"
              :label="i[(item.options && item.options.label) || 'label']"
            />
          </el-select>
          <!-- 联级选择器 cascaderSelect -->
          <el-cascader
            v-if="item.component === 'cascaderSelect'"
            v-model="form[item.field]"
            v-bind="item.componentProps"
            :options="item.optionsArr"
            @change="(value) => changeCascader(item, value)"
            style="width: 100%"
          />
        </el-form-item>
      </div>
      <div class="col form-action">
        <slot name="formActionLeft"></slot>
        <el-form-item>
          <el-button type="primary" @click="handleSearch"> 查询 </el-button>
          <el-button type="default" @click="resetSearch" v-if="!hideSearchFormReset">
            重置
          </el-button>
          <slot name="actions" :record="form"></slot>
        </el-form-item>
      </div>
    </div>
  </el-form>
</template>

<script setup lang="ts">
import { ref, reactive, computed, nextTick } from 'vue'
import { formatDate } from '@/utils/formatTime'
import type { FormSchemaItem } from './types'
import type { FormInstance, FormRules } from 'element-plus'

defineOptions({ name: 'BasicForm' })

const props = withDefaults(
  defineProps<{
    formSchema?: FormSchemaItem[]
    hideSearchFormReset?: boolean
  }>(),
  {
    formSchema: () => [],
    hideSearchFormReset: false
  }
)
const emit = defineEmits<{
  (e: 'search', data: Record<string, any>): void
}>()

const form = reactive<Record<string, any>>({})
const apiSelect_options = reactive({
  params: {},
  responseStructure: 'data',
  label: 'name',
  value: 'id'
})
const formRef = ref<FormInstance>()

const getFormItem = computed(() => {
  console.log('2025年07月30日10:52:34', props.formSchema)
  return (props.formSchema ?? []).map((item: any) => {
    if (item.defaultValue !== null && item.defaultValue !== undefined) {
      form[item.field] = item.defaultValue
    }
    if (item.component == 'ApiSelect') {
      const { responseStructure, params } = { ...apiSelect_options, ...item.options }
      const api = item.options?.api
      getOptions(api, params, responseStructure).then((res: any[]) => {
        item[`${item.field}_options`] = res.map((item) => {
          if (typeof item !== 'object') {
            return {
              label: item,
              value: item
            }
          } else {
            return item
          }
        })
      })
    } else if (item.component == 'cascaderSelect') {
      if (item.options) {
        const { responseStructure, params } = { ...apiSelect_options, ...item.options }
        const api = item.options?.api
        getOptions(api, params, responseStructure).then((res: any[]) => {
          let options = res.map((item) => {
            if (typeof item !== 'object') {
              return {
                label: item,
                value: item
              }
            } else {
              return item
            }
          })
          if (item.options?.afterFetch) {
            options = item.options.afterFetch(options)
          }
          item.optionsArr = options
        })
      } else {
        item.optionsArr = item.componentProps?.options || []
      }
    }
    return item
  })
})

const resetSearch = () => {
  const formEl = formRef.value
  if (!formEl) return
  formEl.resetFields()
  for (let i in form) {
    let item = form[i]
    if (item instanceof Array && item[0] == undefined) {
      form[i] = undefined
    }
  }
  emit('search', { ...form })
}

const handleSearch = () => {
  const formEl = formRef.value
  if (!formEl) return
  for (let i in form) {
    let item = form[i]
    if (item instanceof Array && item[0] == undefined) {
      form[i] = undefined
    }
  }
  formEl.validate((valid: boolean) => {
    if (valid) {
      emit('search', { ...form })
    }
  })
}

const setFormItemOption = (param: { field: string; value: any[] }) => {
  return new Promise((resolve) => {
    const index = props.formSchema.findIndex((item) => item.field === param.field)
    if (index > -1) {
      props.formSchema[index].componentProps!.options = param.value
      resolve({
        code: 200,
        data: props.formSchema,
        message: '数据处理完毕，处理后的表单结果结果由data回显'
      })
    } else {
      resolve({
        code: 200,
        data: props.formSchema,
        message: '未找到对应的field'
      })
    }
  })
}

const setFormItemValue = (param: { field: string; value: any }) => {
  return new Promise((resolve) => {
    form[param.field] = param.value
    resolve({
      code: 200,
      data: form,
      message: '数据处理完毕，处理后的表单结果结果由data回显'
    })
  })
}

const getOptions = (api: (params: any) => Promise<any>, params: any, responseStructure: string) => {
  return new Promise((resolve, reject) => {
    api({ ...params })
      .then((res) => {
        resolve(onjectKeyPathValue(res, responseStructure))
      })
      .catch(() => {
        reject([])
      })
  })
}

const selectChange = (item: FormSchemaItem) => {
  let fieldValue = form[item.field]
  if (
    item.componentProps?.multiple &&
    fieldValue.length &&
    fieldValue[fieldValue.length - 1] == '全部'
  ) {
    let list = []
    if (item.component == 'Select') {
      list = item.componentProps?.options.map((i: any) => {
        return i[(item.options && item.options.value) || 'value']
      })
    } else {
      list = item[`${item.field}_options`].map((i: any) => {
        return i[(item.options && item.options.value) || 'value']
      })
    }
    form[item.field] = []
    form[item.field] = list
  }
  nextTick(() => {}) // $forceUpdate 替代
  if (item && item.componentProps && item.componentProps.onOptionsChange) {
    item.componentProps.onOptionsChange(fieldValue)
  }
}
const changeCascader = (item: FormSchemaItem, data: any) => {
  if (item.componentProps && item.componentProps.change) {
    form[item.field] = item.componentProps.change(data, item)
  } else {
    form[item.field] = data
  }
}
const datepickerChange = (value: any, item: FormSchemaItem) => {
  if (item && item.componentProps && item.componentProps.onChange) {
    item.componentProps.onChange(value)
  }
  nextTick(() => {}) // $forceUpdate 替代
}
const onjectKeyPathValue = (target: any, keyPath: string, value?: any) => {
  if (!keyPath || typeof keyPath !== 'string') return target
  const keys = keyPath.split('.')
  let m = target
  for (let i = 0; i < keys.length - 1; i++) {
    m = m[keys[i]]
  }
  if (typeof value !== 'undefined') {
    m[keys[keys.length - 1]] = value
    return
  }
  m = m[keys[keys.length - 1]]
  return m
}

defineExpose({ resetSearch, handleSearch, setFormItemOption, setFormItemValue })
</script>

<style lang="scss">
.basic-form.el-form--inline .el-form-item.el-form-item--medium {
  display: flex;
  width: 100%;

  > label {
    flex-shrink: 0;
    flex-grow: 0;
    line-height: 1.5;
    display: flex;
    justify-content: end;
    align-items: center;
    text-align: center;
  }

  .el-form-item__content {
    flex: 1 1 100%;
  }
}

.basic-form.el-form--inline .form-action {
  max-width: 100%;

  .el-form-item.el-form-item--medium {
    flex-wrap: wrap;
  }
}
</style>
<style lang="scss" scoped>
.row {
  display: flex;
  justify-content: start;
  align-items: center;
  width: 100%;
  flex-wrap: wrap;
}

.col {
  flex: 0 0 33.33%;
}

.form-action {
  margin-left: auto;
  text-align: right;
  flex-shrink: 0;
  flex-grow: 0;
  flex-basis: auto;
}
</style>
